package com.gitee.fastmybatis.core.mapper;

import com.gitee.fastmybatis.core.PageInfo;
import com.gitee.fastmybatis.core.ext.code.util.FieldUtil;
import com.gitee.fastmybatis.core.query.Query;
import com.gitee.fastmybatis.core.support.PageEasyui;
import com.gitee.fastmybatis.core.util.ClassUtil;
import com.gitee.fastmybatis.core.util.MapperUtil;
import com.gitee.fastmybatis.core.util.MyBeanUtil;
import org.apache.ibatis.annotations.Param;
import org.springframework.beans.BeanUtils;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;

/**
 * 具备查询功能的Mapper
 * 
 * @param <E> 实体类，如：Student
 * @param <I> 主键类型，如：Long，Integer
 * @author tanghc
 */
public interface SchMapper<E, I> extends Mapper<E> {

    /**
     * 内置resultMap名称
     */
    String BASE_RESULT_MAP = "baseResultMap";

    /**
     * 根据主键查询
     * 
     * @param id
     *            主键值
     * @return 返回实体对象，没有返回null
     */
    E getById(I id);

    /**
     * 根据主键查询强制查询，忽略逻辑删除字段
     * @param id id值
     * @return 返回实体对象，没有返回null
     */
    E forceById(I id);

    /**
     * 根据条件查找单条记录
     * 
     * @param query
     *            查询条件
     * @return 返回实体对象，没有返回null
     */
    E getByQuery(@Param("query") Query query);

    /**
     * 根据字段查询一条记录
     * 
     * @param column
     *            数据库字段名
     * @param value
     *            字段值
     * @return 返回实体对象，没有返回null
     */
    E getByColumn(@Param("column") String column, @Param("value") Object value);

    /**
     * 查询总记录数
     * 
     * @param query
     *            查询条件
     * @return 返回总记录数
     */
    long getCount(@Param("query") Query query);

    /**
     * 根据字段查询结果集
     * 
     * @param column
     *            数据库字段名
     * @param value
     *            字段值
     * @return 返回实体对象集合，没有返回空集合
     */
    List<E> listByColumn(@Param("column") String column, @Param("value") Object value);

    /**
     * 查询结果集
     *
     * @param query
     *            查询条件
     * @return 返回实体对象集合，没有返回空集合
     */
    List<E> list(@Param("query") Query query);

    /**
     * 根据字段多个值查询结果集
     *
     * @param column
     *            数据库字段名
     * @param values
     *            多个字段值
     * @return 返回实体对象集合，没有返回空集合
     */
    default List<E> listByArray(String column, Object[] values) {
        if (values == null || values.length == 0) {
            return new ArrayList<>(0);
        }
        Query query = new Query()
                .in(column, values);
        return list(query);
    }

    /**
     * 根据字段多个值查询结果集
     *
     * @param column
     *            数据库字段名
     * @param values
     *            多个字段值
     * @return 返回实体对象集合，没有返回空集合
     */
    default List<E> listByCollection(String column, Collection<?> values) {
        if (values == null || values.isEmpty()) {
            return new ArrayList<>(0);
        }
        Query query = new Query()
                .in(column, values);
        return list(query);
    }

    /**
     * 查询指定字段结果，Map里面key对应字段名，value对应值<br>
     * 不推荐使用，请使用 {@link SchMapper#listBySpecifiedColumns(java.util.List, com.gitee.fastmybatis.core.query.Query) }
     * @param columns
     *            返回的字段
     * @param query
     *            查询条件
     * @return 返回结果集，没有则返回空list
     * @see #listBySpecifiedColumns(List, Query)
     */
    @Deprecated
    default List<Map<String, Object>> listMap(@Param("columns") List<String> columns, @Param("query") Query query) {
        return this.listBySpecifiedColumns(columns, query)
                .stream()
                .map(row -> {
                    Map<String, Object> map = MyBeanUtil.pojoToMap(row);
                    // 兼容之前版本，添加下划线数据
                    Map<String, Object> mapUnderline = new HashMap<>(map.size() * 2);
                    for (Map.Entry<String, Object> entry : map.entrySet()) {
                        String underline = FieldUtil.camelToUnderline(entry.getKey());
                        mapUnderline.put(underline, entry.getValue());
                    }
                    if (!mapUnderline.isEmpty()) {
                        map.putAll(mapUnderline);
                    }
                    return map;
                })
                .collect(Collectors.toList());
    }

    /**
     * 指定列分页查询<br>
     * 不推荐使用，请使用 {@link SchMapper#pageBySpecifiedColumns(java.util.List, com.gitee.fastmybatis.core.query.Query, java.lang.Class)}
     * @param columns 数据库列
     * @param query 查询条件
     * @return 返回分页信息
     * @see #pageBySpecifiedColumns(List, Query, Class)
     */
    @Deprecated
    default PageInfo<Map<String, Object>> pageMap(@Param("columns") List<String> columns, @Param("query") Query query) {
        return MapperUtil.query(columns, this, query);
    }

    /**
     * 查询返回指定的列，返回实体类集合
     * @param columns 指定字段，数据库字段名
     * @param query 查询条件
     * @return 返回实体类集合，没有则返回空list
     */
    List<E> listBySpecifiedColumns(@Param("columns") List<String> columns, @Param("query") Query query);

    /**
     * 查询返回指定的列，返指定类集合
     * @param columns 指定字段，数据库字段名
     * @param query 查询条件
     * @param clazz 集合元素类型，可以是对象class，也可以是基本类型class，如：UserVO.class, Integer.class, String.class。<br>
     *              当指定基本类型class时，<code>columns</code>参数只能指定一列
     * @return 返回实体类集合，没有则返回空list
     * @see #pageBySpecifiedColumns(List, Query, Class) 分页查询
     */
    default <T> List<T> listBySpecifiedColumns(List<String> columns, Query query, Class<T> clazz) {
        if (columns == null || columns.isEmpty()) {
            throw new IllegalArgumentException("未指定columns");
        }
        List<E> list = this.listBySpecifiedColumns(columns, query);
        if (list == null) {
            return new ArrayList<>(0);
        }
        // 如果是单值
        if (ClassUtil.isPrimitive(clazz.getSimpleName())) {
            return MyBeanUtil.listToValueList(list, clazz, columns.get(0));
        }
        return MyBeanUtil.deepCopy(list, clazz);
    }

    /**
     * 查询返回指定的列，返回分页数据
     * @param columns 数据库列名
     * @param query 查询条件
     * @param clazz 元素class
     * @param <T>  元素类
     * @return 返回分页信息
     */
    default <T> PageInfo<T> pageBySpecifiedColumns(List<String> columns, Query query, Class<T> clazz) {
        if (columns == null || columns.isEmpty()) {
            throw new IllegalArgumentException("未指定columns");
        }
        return MapperUtil.query(columns, this, query, clazz);
    }

    /**
     * 分页查询
     * @param query 查询条件
     * @return 返回分页信息
     */
    default PageInfo<E> page(Query query) {
        return MapperUtil.query(this, query);
    }

    /**
     * 查询结果集，并转换结果集中的记录
     * @param query 查询条件
     * @param clazz 结果集转换成指定的class类（通过属性拷贝）
     * @return 返回分页信息
     * @deprecated 使用page(Query query, Supplier<T> supplier)
     * @see #page(Query, Supplier)
     */
    @Deprecated
    default <T> PageInfo<T> page(Query query, Class<T> clazz) {
        return MapperUtil.queryAndConvert(this, query, clazz);
    }

    /**
     * 查询结果集，并转换结果集中的记录
     * @param query 查询条件
     * @param target 转换类
     * @return 返回分页信息
     */
    default <T> PageInfo<T> page(Query query, Supplier<T> target) {
        return MapperUtil.queryAndConvert(this, query, target);
    }

    /**
     * 查询结果集，并转换结果集中的记录，转换处理没一行
     * @param query 查询条件
     * @param converter 转换类
     * @return 返回分页信息
     */
    default <R> PageInfo<R> page(Query query, Function<E, R> converter) {
        PageInfo pageInfo = this.page(query);
        List<E> list = (List<E>) pageInfo.getList();
        List<R> retList = list.stream()
                .map(converter)
                .collect(Collectors.toList());
        pageInfo.setList(retList);
        return (PageInfo<R>) pageInfo;
    }

    /**
     * 查询结果集，并转换结果集中的记录，转换处理list
     * @param query 查询条件
     * @param converter 转换类
     * @return 返回分页信息
     * @since 1.10.11
     */
    default <R> PageInfo<R> pageAndConvert(Query query, Function<List<E>, List<R>> converter) {
        PageInfo pageInfo = this.page(query);
        List<E> list = (List<E>) pageInfo.getList();
        List<R> retList = converter.apply(list);
        pageInfo.setList(retList);
        return (PageInfo<R>) pageInfo;
    }

    /**
     * 查询结果集，并转换结果集中的记录，并对记录进行额外处理
     * @param query 查询条件
     * @param target 转换后的类
     * @param format 对转换后的类格式化，此时的对象已经完成属性拷贝
     * @param <R> 结果集类型
     * @return 返回PageInfo对象
     */
    default <R> PageInfo<R> page(Query query, Supplier<R> target, Consumer<R> format) {
        return this.page(query, t -> {
            R r = target.get();
            BeanUtils.copyProperties(t, r);
            format.accept(r);
            return r;
        });
    }

    /**
     * 查询返回easyui结果集
     * @param query 查询条件
     * @return 返回easyui分页信息
     */
    default PageEasyui<E> pageEasyui(Query query) {
        return MapperUtil.queryForEasyuiDatagrid(this, query);
    }

    /**
     * 查询返回easyui结果集，并转换结果集中的记录
     * @param query 查询条件
     * @param clazz 结果集转换成指定的class类（通过属性拷贝）
     * @return 返回easyui分页信息
     */
    default <T> PageEasyui<T> pageEasyui(Query query, Class<T> clazz) {
        return MapperUtil.queryForEasyuiDatagrid(this, query, clazz);
    }
}
